#include <cstdio>
#include <ctime>
#include <exception>
#include <fmt/format.h>
#include <iostream>
#include <source_location>
#include <string>
using namespace std;

class FileError : public std::exception {
public:
  FileError(const source_location &location) : m_location(location) {}
  const char *what() const noexcept override { return m_message.c_str(); }
  virtual const source_location &where() const noexcept { return m_location; }

protected:
  virtual void setMessage(string message) { m_message = message; }

private:
  string m_message;
  source_location m_location;
};

class FileOpenError : public FileError {
public:
  FileOpenError(string message,
                const source_location &location = source_location::current())
      : FileError(location) {
    setMessage("File open error!" + message);
  }
};
class FileReadError : public FileError {
public:
  FileReadError(string message,
                const source_location &location = source_location::current())
      : FileError{location} {
    setMessage(
        fmt::format("Error reading {},line {}.", message, location.line()));
  }
};

void doSomething() { throw FileOpenError("Throw file open error!"); }

int main() {
  try {
    doSomething();
  } catch (const FileError &e) {
    const auto &location{e.where()};
    cerr << fmt::format("Caught: '{}' at line '{}' in '{}'", e.what(),
                        location.line(), location.function_name())
         << endl;
  }
}